import { View, Text, ViewProps } from "@tarojs/components";
import { ReactNode, useMemo } from "react";
import Steps from "../Steps";
import StatusConteiner from "../StatusConteiner";
import { WarnIcon } from "../Icons";
import "./index.less";
import { TStatus } from "@/types";

type MarkdownNode = {
  type: "h2" | "code" | "ul" | "ol" | "text" | "p";
  content: ReactNode | string;
  children?: MarkdownNode[];
  indent?: number;
  status?: TStatus;
};

type TOptions = {
  isSkipCode?: boolean;
  status?: TStatus;
};

const headingRegex = /^(#{1,3})\s(.+)$/;
const ulRegex = /^-\s(.+)$/;
const olRegex = /^(\d+)\.\s(.+)$/;
const codeFenceRegex = /^(```)([\w-]*)$/;

type Props = {
  content: string;
  className?: string;
} & ViewProps;

export default function MarkdownRenderer({
  content,
  className = "",
  ...others
}: Props) {
  const parseMarkdownContent = (
    lines,
    options: TOptions = {
      isSkipCode: false,
      status: "default",
    }
  ) => {
    const nodes: MarkdownNode[] = [];
    let currentList: MarkdownNode | null = null;
    let codeContent: string[] = [];
    let inCodeBlock = false;
    let status = options.status;

    const ulMatcher = (line: string) => {
      const ulMatch = line.match(ulRegex);
      if (ulMatch) {
        if (!currentList || currentList.type !== "ul") {
          currentList = { type: "ul", content: null, children: [] };
          nodes.push(currentList);
        }
        currentList.children?.push({
          type: "text",
          content: ulMatch[1],
        });
        return true;
      }
    };

    const olMatcher = (line: string) => {
      const olMatch = line.match(olRegex);
      if (olMatch) {
        if (!currentList || currentList.type !== "ol") {
          currentList = { type: "ol", content: null, children: [] };
          nodes.push(currentList);
        }
        currentList.children?.push({
          type: "text",
          content: `${olMatch[2]}`,
        });
        return true;
      }
    };

    const headMatcher = (line: string) => {
      const headingMatch = line.match(headingRegex);
      if (headingMatch) {
        nodes.push({
          type: `h2`,
          content: headingMatch[2],
          status,
        });
        return true;
      }
    };

    const codeMatcher = (line: string) => {
      if (options.isSkipCode) return false;
      const codeFenceMatch = codeFenceRegex.exec(line);

      if (codeFenceMatch) {
        if (!inCodeBlock) {
          inCodeBlock = true;
          codeContent = [];
          if (codeFenceMatch?.[2]) {
            status = codeFenceMatch?.[2] as TStatus;
          }
          return true;
        } else {
          nodes.push({
            type: "code",
            content: null,
            status,
            children: parseMarkdownContent(codeContent, {
              isSkipCode: true,
              status,
            }),
          });
          inCodeBlock = false;
          status = "default";
          return true;
        }
      }

      if (inCodeBlock) {
        codeContent.push(line);
        return true;
      }
    };

    for (const line of lines) {
      if (codeMatcher(line)) continue;
      if (headMatcher(line)) continue;
      if (ulMatcher(line)) continue;
      if (olMatcher(line)) continue;
      if (line !== "") {
        currentList = { type: "p", content: line };
        nodes.push(currentList);
      }
      currentList = null;
    }

    return nodes;
  };

  const parsedNodes = useMemo(() => {
    const lines = content.split("\n");
    return parseMarkdownContent(lines);
  }, [content]);

  const renderNode = (node: MarkdownNode, index: number) => {
    switch (node.type) {
      case "h2":
        return (
          <View className="heading">
            {node.status === "warning" && <WarnIcon type="warning" />}
            <Text key={`h-${index}`} className={node.type}>
              {node.content}
            </Text>
          </View>
        );
      case "p":
        return (
          <Text key={`p-${index}`} className="paragraph">
            {node.content}
          </Text>
        );
      case "ul":
        return (
          <View key={`ul-${index}`} className="unordered-list">
            {node.children?.map((child, i) => (
              <View key={`uli-${i}`} className="list-item">
                <Text className="list-bullet">•</Text>
                <Text className="list-content">{child.content}</Text>
              </View>
            ))}
          </View>
        );

      case "ol":
        return (
          <Steps
            options={node.children?.map((item) => item.content) as string[]}
          />
        );
      case "code":
        return (
          <StatusConteiner
            style={{ marginTop: "16px" }}
            type={node.status || "default"}
          >
            {node.children?.map(renderNode)}
          </StatusConteiner>
        );
      default:
        return (
          <Text key={`text-${index}`} className="text-content">
            {node.content}
          </Text>
        );
    }
  };

  return (
    <View className={`mdrenderer ${className}`} {...others}>
      {parsedNodes.map(renderNode)}
    </View>
  );
}
